package com.logic.landseaserver.models.prepayment.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.logic.common.domain.Code;
import com.logic.common.security.SecurityUtils;
import com.logic.common.util.BillTypeZhUtil;
import com.logic.common.util.CurrentThread;
import com.logic.common.util.QueryUtil;
import com.logic.landseaserver.common.LandeaConstants;
import com.logic.landseaserver.common.LandeaConstants.AccountStatus;
import com.logic.landseaserver.common.LandeaConstants.BalanceType;
import com.logic.landseaserver.common.LandeaConstants.BillType;
import com.logic.landseaserver.common.LandeaConstants.CouponStatus;
import com.logic.landseaserver.common.LandeaConstants.MoneyType;
import com.logic.landseaserver.common.LandeaConstants.QueryUesdCodeType;
import com.logic.landseaserver.common.LandeaConstants.ReRentType;
import com.logic.landseaserver.common.config.FileConfiguration;
import com.logic.landseaserver.common.enums.BillStatusEnum;
import com.logic.landseaserver.common.enums.BillTypeEnum;
import com.logic.landseaserver.common.enums.ContractStatusCodeEnum;
import com.logic.landseaserver.common.enums.DepositEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.AccountResultCode;
import com.logic.landseaserver.common.exception.ResultCodes.AuthorizationResultCode;
import com.logic.landseaserver.common.exception.ResultCodes.PaymentResultCode;
import com.logic.landseaserver.common.util.Arith;
import com.logic.landseaserver.common.util.DateUtil;
import com.logic.landseaserver.common.util.LandSeaUtil;
import com.logic.landseaserver.common.util.StringTools;
import com.logic.landseaserver.domain.Account;
import com.logic.landseaserver.domain.AccountBalance;
import com.logic.landseaserver.domain.AccountChargeback;
import com.logic.landseaserver.domain.AccountRecharge;
import com.logic.landseaserver.domain.Bill;
import com.logic.landseaserver.domain.BillPayment;
import com.logic.landseaserver.domain.Contract;
import com.logic.landseaserver.domain.CoupUsedRecord;
import com.logic.landseaserver.domain.Coupon;
import com.logic.landseaserver.domain.PreOffrentDetailBill;
import com.logic.landseaserver.domain.Project;
import com.logic.landseaserver.models.independent.service.BillIndependentService;
import com.logic.landseaserver.models.payment.PaymentConstant.BillPaymentStatus;
import com.logic.landseaserver.models.payment.PaymentConstant.BillTypeCode;
import com.logic.landseaserver.models.payment.PaymentConstant.PayType;
import com.logic.landseaserver.models.prepayment.bean.AccountBalanceResp;
import com.logic.landseaserver.models.prepayment.bean.AccountChargebackResp;
import com.logic.landseaserver.models.prepayment.bean.AccountDetailResp;
import com.logic.landseaserver.models.prepayment.bean.AccountRechargeResp;
import com.logic.landseaserver.models.prepayment.bean.AccountResp;
import com.logic.landseaserver.models.prepayment.bean.AcotBalaForWCResp;
import com.logic.landseaserver.models.prepayment.bean.AddAccountObject;
import com.logic.landseaserver.models.prepayment.bean.AuthCodeIntoReq;
import com.logic.landseaserver.models.prepayment.bean.CurrentAccount;
import com.logic.landseaserver.models.prepayment.bean.SelectEffectiveCouponReq;
import com.logic.landseaserver.persistence.read.AccountBalanceReadMapper;
import com.logic.landseaserver.persistence.read.AccountChargebackReadMapper;
import com.logic.landseaserver.persistence.read.AccountReadMapper;
import com.logic.landseaserver.persistence.read.AccountRechargeReadMapper;
import com.logic.landseaserver.persistence.read.BillReadMapper;
import com.logic.landseaserver.persistence.read.ContractReadMapper;
import com.logic.landseaserver.persistence.read.CouponReadMapper;
import com.logic.landseaserver.persistence.read.ProjectReadMapper;
import com.logic.landseaserver.persistence.write.AccountBalanceWriteMapper;
import com.logic.landseaserver.persistence.write.AccountChargebackWriteMapper;
import com.logic.landseaserver.persistence.write.AccountRechargeWriteMapper;
import com.logic.landseaserver.persistence.write.AccountWriteMapper;
import com.logic.landseaserver.persistence.write.BillPaymentWriteMapper;
import com.logic.landseaserver.persistence.write.BillWriteMapper;
import com.logic.landseaserver.persistence.write.CoupUsedRecordWriteMapper;
import com.logic.landseaserver.persistence.write.CouponWriteMapper;
import com.logic.landseaserver.ws.dto.QueryCouponUesdDTO;
import com.logic.landseaserver.ws.response.ExpenseTypeResp;
import com.logic.system.service.ICodeService;
import com.logic.system.service.IConfigurationService;
import com.logic.system.service.impl.ConfigurationService;

/**
 * 
 *
 * 项目名称：system-server</br>
 * 类名称：PrepaymentService</br>
 * 类描述：预付款</br>
 * 创建人：Crystal</br>
 * 创建时间：2017年11月28日 下午5:15:40</br>
 * 
 * @version 1.0
 *
 */
@Service
public class PrepaymentService
{
    
    private static final Logger LOGGER = LoggerFactory.getLogger(PrepaymentService.class);
    
    /** 换房A合同生成的账单在线程变量中的key **/
    public static final String CHANGE_ROOM_A_BILL = "CHANGE_ROOM_A_BILL";
    
    @Autowired
    private AccountReadMapper accountReadMapper;
    
    @Autowired
    private AccountWriteMapper accountWriteMapper;
    
    @Autowired
    private AccountBalanceWriteMapper accountBalanceWriteMapper;
    
    @Autowired
    private AccountBalanceReadMapper accountBalanceReadMapper;
    
    @Autowired
    private AccountRechargeWriteMapper accountRechargeWriteMapper;
    
    @Autowired
    private AccountChargebackWriteMapper accountChargebackWriteMapper;
    
    @Autowired
    private CouponReadMapper couponReadMapper;
    
    @Autowired
    private BillReadMapper billReadMapper;
    
    @Autowired
    private BillWriteMapper billWriteMapper;
    
    @Autowired
    private AccountChargebackReadMapper accountChargebackReadMapper;
    
    @Autowired
    private AccountRechargeReadMapper accountRechargeReadMapper;
    
    @Autowired
    private BillPaymentWriteMapper billPaymentWriteMapper;
    
    @Autowired
    private CouponWriteMapper couponWriteMapper;
    
    @Autowired
    private CoupUsedRecordWriteMapper coupUsedRecordWriteMapper;
    
    @Autowired
    private FileConfiguration fileConfiguration;
    
    @Autowired
    private ContractReadMapper contractReadMapper;
    
    @Autowired
    private BillIndependentService billIndependentService;
    
    @Autowired
    private IConfigurationService configurationService;
    
    @Autowired
    private ProjectReadMapper projectReadMapper;
    
    @Autowired
    private AutoPayBillService autoPayBillService;
    
    @Autowired
    private ICodeService codeService;
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public CurrentAccount addAccount(AddAccountObject object)
        throws LandseaException
    {
        try
        {
            CurrentAccount currentAccount = new CurrentAccount();
            List<AccountBalance> balanceList = null;
            
            Account saveAccount = new Account();
            Account previousAccount = accountReadMapper.selectAccount(object.getRoomId(), object.getUserId());
            // 还没有创建过账户
            if (null == previousAccount)
            {
                saveAccount.setWid(StringTools.generateUuid());
                saveAccount.setRoomId(object.getRoomId());
                saveAccount.setUserId(object.getUserId());
                saveAccount.setProjectId(object.getProjectId());
                saveAccount.setStatus(AccountStatus.NORMAL);
                saveAccount.initCommonField();
                accountWriteMapper.insertSelective(saveAccount);
                currentAccount.setAccount(saveAccount);
                // 创建账户余额表
                balanceList = addAccountBalance(saveAccount.getId(), object.getProjectId());
            }
            else
            {
                currentAccount.setAccount(previousAccount);
                
                Contract contract = contractReadMapper.selectByPrimaryKey(object.getContractId());
                // 续租情况等junny那边加好字段
                if (ReRentType.RERENT.equals(contract.getRentType()) && null != contract.getOriginalCId())
                {
                    return currentAccount;
                }
                
                // 隔半年住一下
                // 创建账户余额表
                else
                {
                    // 如果不是正常，则更新成正常
                    if (!AccountStatus.NORMAL.equals(previousAccount.getStatus()))
                    {
                        updateAccountStatusById(previousAccount.getId(), AccountStatus.NORMAL);
                    }
                    
                    accountBalanceWriteMapper.deleteByPhysical(previousAccount.getId());
                    
                    AccountRecharge accountRecharge = new AccountRecharge();
                    accountRecharge.setAccountId(previousAccount.getId());
                    accountRecharge.editInitCommonField();
                    accountRechargeWriteMapper.deleteByAccountId(accountRecharge);
                    
                    AccountChargeback accountChargeback = new AccountChargeback();
                    accountChargeback.setAccountId(previousAccount.getId());
                    accountChargeback.editInitCommonField();
                    accountChargebackWriteMapper.deleteByAccountId(accountChargeback);
                    
                    balanceList = addAccountBalance(previousAccount.getId(), object.getProjectId());
                }
                
            }
            currentAccount.setBalanceList(balanceList);
            
            return currentAccount;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("addAccount|新增账户失败.", e);
            throw LandseaException.createException(AccountResultCode.E00950004);
        }
    }
    
    /**
     * 
     * [简要描述]：根据配置新增余额项</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-25)</br>
     *
     * @param accountId
     * @param projectId 暂时没用了
     * @return
     * @throws LandseaException
     *
     */
    private List<AccountBalance> addAccountBalance(Integer accountId, Integer projectId)
        throws LandseaException
    {
        try
        {
            List<AccountBalance> balanceList = new ArrayList<AccountBalance>();
            
            // 查询余额费项配置项
            String accountFeeTypes = configurationService.getStringValue(ConfigurationService.ACCOUNT_BALANCE_ALL_FEE);
            if (StringUtils.isEmpty(accountFeeTypes))
            {
                LOGGER.info("addAccountBalance|没有配置余额项费项，新增余额项结束.AccountId=" + accountId);
                return balanceList;
            }
            
            LOGGER.info("addAccountBalance|配置待新增的余额项为" + accountFeeTypes);
            
            String[] accountBalanceTypeArray = accountFeeTypes.split(",");
            if (ArrayUtils.isEmpty(accountBalanceTypeArray))
            {
                LOGGER.info("addAccountBalance|没有配置余额项费项，新增余额项结束.AccountId=" + accountId);
                return balanceList;
            }
            
            // 查询所有费项
            List<Code> allFeeCodeList = codeService.getChildCodes("_CTBI");
            if (CollectionUtils.isEmpty(allFeeCodeList))
            {
                LOGGER.info("addAccountBalance|当前系统没有配置费项，新增余额项结束.AccountId=" + accountId);
                return balanceList;
            }
            
            List<String> codeValueList = new ArrayList<String>();
            for (Code code : allFeeCodeList)
            {
                codeValueList.add(code.getCode());
            }
            
            AccountBalance saveAccountBalance = null;
            for (String balanceType : accountBalanceTypeArray)
            {
                // 只新增合法的,通用除外
                if (!BalanceType.COMMON.equals(balanceType) && !codeValueList.contains(balanceType))
                {
                    LOGGER.error("addAccountBalance|配置项余额项" + balanceType + "非法，不可新增.");
                    continue;
                }
                
                saveAccountBalance = new AccountBalance();
                saveAccountBalance.setWid(StringTools.generateUuid());
                saveAccountBalance.setAccountId(accountId);
                saveAccountBalance.setBalanceType(balanceType);
                saveAccountBalance.setMoney(0.00d);
                saveAccountBalance.setFreezeMoney(0.00d);
                saveAccountBalance.initCommonField();
                balanceList.add(saveAccountBalance);
                accountBalanceWriteMapper.insertSelective(saveAccountBalance);
            }
            
            return balanceList;
        }
        catch (Exception e)
        {
            LOGGER.error("addAccountBalance|新增账户余额项失败.", e);
            throw LandseaException.createException(AccountResultCode.E00950007);
        }
    }
    
    /**
     * 
     * [简要描述]：根据项目的费项新增账户余额项</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-25)</br>
     *
     * @param accountId
     * @param projectId
     * @return
     * @throws LandseaException
     *
     */
    /*
     * private List<AccountBalance> addAccountBalance(Integer accountId, Integer projectId) throws LandseaException {
     * try { List<AccountBalance> balanceList = new ArrayList<AccountBalance>();
     * 
     * AccountBalance saveAccountBalance = null; Project project = projectReadMapper.selectByPrimaryKey(projectId); if
     * (null == project) { throw LandseaException.createException(AccountResultCode.E00950005); } if (null ==
     * project.getCostType()) { throw LandseaException.createException(AccountResultCode.E00950006); } List<String>
     * balanceTypeResps = new ArrayList<String>(); balanceTypeResps.add(BalanceType.COMMON);
     * balanceTypeResps.add(BillTypeEnum.CTBI11.getCode()); balanceTypeResps.add(BillTypeEnum.CTBI10.getCode());
     * balanceTypeResps.add(BillTypeEnum.CTBI8.getCode()); balanceTypeResps.add(BillTypeEnum.CTBI9.getCode()); String[]
     * arrs = project.getCostType().split(","); for (String string : arrs) { if ("_".equals(string.subSequence(0, 1))) {
     * if (balanceTypeResps.contains(string)) { continue; } // 排除“其他”“账户余额” if
     * (BillTypeEnum.CTBI12.getCode().equals(string) || BillTypeEnum.CTBI22.getCode().equals(string)) { continue; }
     * balanceTypeResps.add(string); } } for (String balanceType : balanceTypeResps) { saveAccountBalance = new
     * AccountBalance(); saveAccountBalance.setWid(StringTools.generateUuid());
     * saveAccountBalance.setAccountId(accountId); saveAccountBalance.setBalanceType(balanceType);
     * saveAccountBalance.setMoney(0.00d); saveAccountBalance.setFreezeMoney(0.00d);
     * saveAccountBalance.initCommonField(); balanceList.add(saveAccountBalance);
     * accountBalanceWriteMapper.insertSelective(saveAccountBalance); }
     * 
     * return balanceList; } catch (LandseaException e) { throw e; } catch (Exception e) {
     * LOGGER.error("addAccountBalance|新增账户余额项失败.", e); throw
     * LandseaException.createException(AccountResultCode.E00950007); } }
     */
    
    public List<AccountResp> getAccountList(QueryUtil queryUtil)
        throws LandseaException
    {
        try
        {
            List<AccountResp> resultResps = new ArrayList<AccountResp>();
            List<AccountResp> accountResps = new ArrayList<AccountResp>();
            queryUtil.getParams().put("adminId", SecurityUtils.getCurrentLogin().getUserId());
            accountResps = accountReadMapper.selectAccountListByDZ(queryUtil);
            if (CollectionUtils.isNotEmpty(accountResps))
            {
                String userRoomSqlString = buildUserRoomCondition(accountResps);
                Map<String, Object> queryMap = new HashMap<String, Object>();
                queryMap.put("userRoomSqlString", userRoomSqlString);
                // 获取所有帐号的合同
                List<Contract> contractList = contractReadMapper.select4AccountByRmIdAndUrId(queryMap);
                // 构造
                Map<String, List<Contract>> accountContractMap = packAccountContractMap(contractList);
                
                String key = null;
                for (AccountResp accountResp : accountResps)
                {
                    key = accountResp.getRoomId() + "_" + accountResp.getUserId();
                    Contract contract = getEffecitveForAdmin(accountContractMap.get(key));
                    if (null != contract)
                    {
                        accountResp.setContractId(contract.getId());
                        accountResp.setContractStatus(contract.getStatus());
                        accountResp.setContractNo(contract.getContractNo());
                        resultResps.add(accountResp);
                    }
                }
            }
            return resultResps;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountList|查询账户列表失败.", e);
            throw LandseaException.createException(AccountResultCode.E00950001);
        }
    }
    
    private Map<String, List<Contract>> packAccountContractMap(List<Contract> contractList)
    {
        Map<String, List<Contract>> accountContractMap = new HashMap<String, List<Contract>>();
        
        if (CollectionUtils.isEmpty(contractList))
        {
            return accountContractMap;
        }
        
        List<Contract> list = null;
        String key = null;
        for (Contract contract : contractList)
        {
            key = contract.getRoomId() + "_" + contract.getUserId();
            list = accountContractMap.get(key);
            if (null == list)
            {
                list = new ArrayList<Contract>();
                accountContractMap.put(key, list);
            }
            list.add(contract);
        }
        
        return accountContractMap;
    }
    
    /**
     * 
     * [简要描述]：拼接room_id_userId</br>
     * [详细描述]：例如：'123_321','321_123'</br>
     * [作者]：Aaron(2018-01-12)</br>
     *
     * @param accountResps
     * @return
     *
     */
    private String buildUserRoomCondition(List<AccountResp> accountResps)
    {
        StringBuilder stringBuilder = new StringBuilder();
        int size = accountResps.size();
        for (int i = 0; i < size; i++)
        {
            stringBuilder.append("'" + accountResps.get(i).getRoomId());
            stringBuilder.append("_");
            stringBuilder.append(accountResps.get(i).getUserId() + "'");
            
            if (i != size - 1)
            {
                stringBuilder.append(",");
            }
        }
        return stringBuilder.toString();
    }
    
    public Contract getEffecitveForAdmin(List<Contract> contracts)
    {
        Contract resultContract = new Contract();
        if (CollectionUtils.isEmpty(contracts))
        {
            return resultContract;
        }
        
        // 先查询除了删除之外所有的合同，查询出来直接是按照ID升序排列
        for (Contract contract : contracts)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            if (ContractStatusCodeEnum.SIGN.getCode().equals(contract.getStatus()))
            {
                resultContract.setId(contract.getId());
                resultContract.setStatus(contract.getStatus());
                resultContract.setContractNo(contract.getContractNo());
                resultContract.setInDate(contract.getInDate());
                resultContract.setOutDate(contract.getOutDate());
                return resultContract;
            }
        }
        
        int size = contracts.size();
        
        // 没有签约中的,查看是否有草稿的合同,返回最新的草稿合同
        for (int i = size - 1; i >= 0; i--)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            Contract contract = contracts.get(i);
            if (ContractStatusCodeEnum.NEW.getCode().equals(contract.getStatus()))
            {
                resultContract.setId(contract.getId());
                resultContract.setStatus(contract.getStatus());
                resultContract.setContractNo(contract.getContractNo());
                resultContract.setInDate(contract.getInDate());
                resultContract.setOutDate(contract.getOutDate());
                return resultContract;
            }
        }
        
        // 如果没有草稿的，返回最新的结束
        // 没有签约中的,查看是否有草稿的合同,返回最新的草稿合同
        for (int i = size - 1; i >= 0; i--)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            Contract contract = contracts.get(i);
            if (ContractStatusCodeEnum.END.getCode().equals(contract.getStatus()))
            {
                resultContract.setId(contract.getId());
                resultContract.setStatus(contract.getStatus());
                resultContract.setContractNo(contract.getContractNo());
                resultContract.setInDate(contract.getInDate());
                resultContract.setOutDate(contract.getOutDate());
                return resultContract;
            }
        }
        
        return resultContract;
    }
    
    /**
     * 
     * [简要描述]：获取账户管理-账户对应的合同</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-20)</br>
     *
     * @param userId
     * @param roomId
     * @return
     *
     */
    public Contract getEffecitveForAdmin(Integer userId, Integer roomId)
    {
        Contract resultContract = new Contract();
        List<Contract> contracts = contractReadMapper.selectByRmIdAndUrId(userId, roomId);
        if (CollectionUtils.isEmpty(contracts))
        {
            return resultContract;
        }
        
        // 先查询除了删除之外所有的合同，查询出来直接是按照ID升序排列
        for (Contract contract : contracts)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            if (ContractStatusCodeEnum.SIGN.getCode().equals(contract.getStatus()))
            {
                return contract;
            }
        }
        
        int size = contracts.size();
        
        // 没有签约中的,查看是否有草稿的合同,返回最新的草稿合同
        for (int i = size - 1; i >= 0; i--)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            Contract contract = contracts.get(i);
            if (ContractStatusCodeEnum.NEW.getCode().equals(contract.getStatus()))
            {
                return contract;
            }
        }
        
        // 如果没有草稿的，返回最新的结束
        // 没有签约中的,查看是否有草稿的合同,返回最新的草稿合同
        for (int i = size - 1; i >= 0; i--)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            Contract contract = contracts.get(i);
            if (ContractStatusCodeEnum.END.getCode().equals(contract.getStatus()))
            {
                return contract;
            }
        }
        
        return resultContract;
    }
    
    /*
     * public Contract getEffecitveForAdmin(Integer userId, Integer roomId) { Contract resultContract = new Contract();
     * List<Contract> contracts = contractReadMapper.selectByRmIdAndUrId(userId, roomId); if
     * (CollectionUtils.isNotEmpty(contracts)) { // 合同只有一份 if (contracts.size() == 1) {
     * resultContract.setId(contracts.get(0).getId()); resultContract.setStatus(contracts.get(0).getStatus());
     * resultContract.setContractNo(contracts.get(0).getContractNo());
     * resultContract.setInDate(contracts.get(0).getInDate()); resultContract.setOutDate(contracts.get(0).getOutDate());
     * } // 合同只有两份 else if (contracts.size() == 2) { Contract contractA = contracts.get(0); Contract contractB =
     * contracts.get(1); // 两份全是签约中 if (ContractStatusCodeEnum.SIGN.getCode().equals(contractA.getStatus()) &&
     * ContractStatusCodeEnum.SIGN.getCode().equals(contractB.getStatus())) { resultContract.setId(contractA.getId());
     * resultContract.setStatus(contractA.getStatus()); resultContract.setContractNo(contractA.getContractNo());
     * resultContract.setInDate(contractA.getInDate()); resultContract.setOutDate(contractA.getOutDate());
     * 
     * } // 两份全是结束 else if (ContractStatusCodeEnum.END.getCode().equals(contractA.getStatus()) &&
     * ContractStatusCodeEnum.END.getCode().equals(contractB.getStatus())) { resultContract.setId(contractB.getId());
     * resultContract.setStatus(contractB.getStatus()); resultContract.setContractNo(contractB.getContractNo());
     * resultContract.setInDate(contractB.getInDate()); resultContract.setOutDate(contractB.getOutDate()); } //
     * 一份结束了，一份草稿 else if (ContractStatusCodeEnum.END.getCode().equals(contractA.getStatus()) &&
     * ContractStatusCodeEnum.NEW.getCode().equals(contractB.getStatus())) { List<Bill> bills =
     * billReadMapper.queryBillForContract(contractB.getId()); if (bills.size() != 1) {
     * resultContract.setId(contractA.getId()); resultContract.setStatus(contractA.getStatus());
     * resultContract.setContractNo(contractA.getContractNo()); resultContract.setInDate(contractA.getInDate());
     * resultContract.setOutDate(contractA.getOutDate()); } else { resultContract.setId(contractB.getId());
     * resultContract.setStatus(contractB.getStatus()); resultContract.setContractNo(contractB.getContractNo());
     * resultContract.setInDate(contractB.getInDate()); resultContract.setOutDate(contractB.getOutDate()); } } //
     * 一份结束了，一份草稿 else if (ContractStatusCodeEnum.END.getCode().equals(contractB.getStatus()) &&
     * ContractStatusCodeEnum.NEW.getCode().equals(contractA.getStatus())) { List<Bill> bills =
     * billReadMapper.queryBillForContract(contractA.getId()); if (bills.size() != 1) {
     * resultContract.setId(contractB.getId()); resultContract.setStatus(contractB.getStatus());
     * resultContract.setContractNo(contractB.getContractNo()); resultContract.setInDate(contractB.getInDate());
     * resultContract.setOutDate(contractB.getOutDate()); } else { resultContract.setId(contractA.getId());
     * resultContract.setStatus(contractA.getStatus()); resultContract.setContractNo(contractA.getContractNo());
     * resultContract.setInDate(contractA.getInDate()); resultContract.setOutDate(contractA.getOutDate()); } } //
     * 一份草稿一份签约 else if (ContractStatusCodeEnum.SIGN.getCode().equals(contractB.getStatus()) &&
     * ContractStatusCodeEnum.NEW.getCode().equals(contractA.getStatus())) { resultContract.setId(contractB.getId());
     * resultContract.setStatus(contractB.getStatus()); resultContract.setContractNo(contractB.getContractNo());
     * resultContract.setInDate(contractB.getInDate()); resultContract.setOutDate(contractB.getOutDate()); } else if
     * (ContractStatusCodeEnum.SIGN.getCode().equals(contractA.getStatus()) &&
     * ContractStatusCodeEnum.NEW.getCode().equals(contractB.getStatus())) { resultContract.setId(contractA.getId());
     * resultContract.setStatus(contractA.getStatus()); resultContract.setContractNo(contractA.getContractNo());
     * resultContract.setInDate(contractA.getInDate()); resultContract.setOutDate(contractA.getOutDate()); }
     * 
     * // 一份签约一份结束 else if (ContractStatusCodeEnum.SIGN.getCode().equals(contractB.getStatus()) &&
     * ContractStatusCodeEnum.END.getCode().equals(contractA.getStatus())) { Bill bill =
     * billReadMapper.queryLastBill(contractA.getId()); if (BillStatusEnum.BIST2.getCode().equals(bill.getBillStatus()))
     * { resultContract.setId(contractB.getId()); resultContract.setStatus(contractB.getStatus());
     * resultContract.setContractNo(contractB.getContractNo()); resultContract.setInDate(contractB.getInDate());
     * resultContract.setOutDate(contractB.getOutDate()); } else { resultContract.setId(contractA.getId());
     * resultContract.setStatus(contractA.getStatus()); resultContract.setContractNo(contractA.getContractNo());
     * resultContract.setInDate(contractA.getInDate()); resultContract.setOutDate(contractA.getOutDate()); } } else if
     * (ContractStatusCodeEnum.SIGN.getCode().equals(contractA.getStatus()) &&
     * ContractStatusCodeEnum.END.getCode().equals(contractB.getStatus())) { Bill bill =
     * billReadMapper.queryLastBill(contractB.getId()); if (BillStatusEnum.BIST2.getCode().equals(bill.getBillStatus()))
     * { resultContract.setId(contractA.getId()); resultContract.setStatus(contractA.getStatus());
     * resultContract.setContractNo(contractA.getContractNo()); resultContract.setInDate(contractA.getInDate());
     * resultContract.setOutDate(contractA.getOutDate()); } else { resultContract.setId(contractB.getId());
     * resultContract.setStatus(contractB.getStatus()); resultContract.setContractNo(contractB.getContractNo());
     * resultContract.setInDate(contractB.getInDate()); resultContract.setOutDate(contractB.getOutDate()); } } } //
     * 不止两份合同 else { List<String> contractStatus = new ArrayList<String>(); for (Contract contract : contracts) {
     * contractStatus.add(contract.getStatus()); } if (contractStatus.contains(ContractStatusCodeEnum.SIGN.getCode())) {
     * // 取签约中的，老的签约 contracts = contracts.stream().filter(c ->
     * ContractStatusCodeEnum.SIGN.equals(c.getBillStatus())).collect(Collectors.toList()); resultContract =
     * contracts.get(0); return resultContract; } if (contractStatus.contains(ContractStatusCodeEnum.NEW.getCode())) {
     * for (Contract contract : contracts) { if (ContractStatusCodeEnum.NEW.getCode().equals(contract.getStatus())) {
     * List<Bill> bills = billReadMapper.queryBillForContract(contract.getId()); if (CollectionUtils.isNotEmpty(bills))
     * { resultContract.setId(contract.getId()); resultContract.setStatus(contract.getStatus());
     * resultContract.setContractNo(contract.getContractNo()); resultContract.setInDate(contract.getInDate());
     * resultContract.setOutDate(contract.getOutDate()); } else { resultContract = contracts.get(contracts.size() - 2);
     * } }
     * 
     * } } else { resultContract = contracts.get(contracts.size() - 1); } } } return resultContract; }
     */
    
    public Map<String, Object> getAccountDetail(Integer accountId)
        throws LandseaException
    {
        try
        {
            Map<String, Object> resultMap = new HashMap<String, Object>();
            AccountDetailResp resp = accountReadMapper.selectAccountDetail(accountId);
            if (null == resp)
            {
                throw LandseaException.createException(AccountResultCode.E009500012);
            }
            else
            {
                Contract contract = getEffecitveForAdmin(resp.getUserId(), resp.getRoomId());
                if (null != contract)
                {
                    List<ExpenseTypeResp> balanceTypeResps = new ArrayList<ExpenseTypeResp>();
                    List<ExpenseTypeResp> chargebackTypeResps = new ArrayList<ExpenseTypeResp>();
                    resp.setContractId(contract.getId());
                    resp.setContractNo(contract.getContractNo());
                    resp.setContractStatusCode(contract.getStatus());
                    String inDate = DateUtil.convertDate(contract.getInDate());
                    String outDate = DateUtil.convertDate(contract.getOutDate());
                    resp.setContractTime(inDate + "至" + outDate);
                    resultMap.put("accountDetail", resp);
                    List<AccountBalanceResp> accountBalanceResps =
                        accountBalanceReadMapper.selectByAccountId(accountId);
                    for (AccountBalanceResp accountBalanceResp : accountBalanceResps)
                    {
                        ExpenseTypeResp expenseTypeResp = new ExpenseTypeResp();
                        expenseTypeResp.setTypeCode(accountBalanceResp.getBalanceTypeCode());
                        if (BalanceType.COMMON.equals(accountBalanceResp.getBalanceTypeCode()))
                        {
                            expenseTypeResp.setTypeName("通用");
                        }
                        else
                        {
                            expenseTypeResp.setTypeName(accountBalanceResp.getBalanceType());
                        }
                        balanceTypeResps.add(expenseTypeResp);
                    }
                    resultMap.put("balanceType", balanceTypeResps);
                    
                    List<AccountChargebackResp> accountChargebackResps =
                        accountChargebackReadMapper.selectByAccountId(accountId);
                    for (AccountChargebackResp accountChargebackResp : accountChargebackResps)
                    {
                        ExpenseTypeResp expenseTypeResp = new ExpenseTypeResp();
                        expenseTypeResp.setTypeCode(accountChargebackResp.getFeeTypeCode());
                        expenseTypeResp.setTypeName(accountChargebackResp.getFeeType());
                        chargebackTypeResps.add(expenseTypeResp);
                    }
                    resultMap.put("chargebackType", chargebackTypeResps);
                }
                return resultMap;
            }
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountDetail|查询账户详情失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500011);
        }
        
    }
    
    public List<AccountBalanceResp> getAccountBalanceList(QueryUtil queryUtil)
        throws LandseaException
    {
        try
        {
            List<AccountBalanceResp> accountBalanceResps = accountBalanceReadMapper.selectListByAccountId(queryUtil);
            if (CollectionUtils.isNotEmpty(accountBalanceResps))
            {
                for (AccountBalanceResp accountBalanceResp : accountBalanceResps)
                {
                    if ((BalanceType.COMMON).equals(accountBalanceResp.getBalanceTypeCode()))
                    {
                        accountBalanceResp.setBalanceType("通用");
                    }
                }
            }
            return accountBalanceResps;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountBalanceList|查询账户信息列表失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500014);
        }
    }
    
    public List<AccountRechargeResp> getAccountRechargeList(QueryUtil queryUtil)
        throws LandseaException
    {
        try
        {
            List<AccountRechargeResp> accountRechargeResps = accountRechargeReadMapper.selectByAccountId(queryUtil);
            if (CollectionUtils.isNotEmpty(accountRechargeResps))
            {
                for (AccountRechargeResp accountRechargeResp : accountRechargeResps)
                {
                    if ((BalanceType.COMMON).equals(accountRechargeResp.getBalanceTypeCode()))
                    {
                        accountRechargeResp.setBalanceType("通用");
                    }
                }
            }
            return accountRechargeResps;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountRechargeList|查询充值列表失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500016);
        }
    }
    
    public List<AccountChargebackResp> getAccountChargebackList(QueryUtil queryUtil)
        throws LandseaException
    {
        try
        {
            List<AccountChargebackResp> accountChargebackResps =
                accountChargebackReadMapper.selectListByAccountId(queryUtil);
            if (CollectionUtils.isNotEmpty(accountChargebackResps))
            {
                for (AccountChargebackResp accountChargebackResp : accountChargebackResps)
                {
                    if ((BalanceType.COMMON).equals(accountChargebackResp.getBalanceTypeCode()))
                    {
                        accountChargebackResp.setBalanceType("通用");
                    }
                }
            }
            return accountChargebackResps;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountChargebackList|查询扣款列表失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500018);
        }
    }
    
    public List<QueryCouponUesdDTO> canUseAuthCode(SelectEffectiveCouponReq req)
        throws LandseaException
    {
        try
        {
            List<QueryCouponUesdDTO> canUsedCouponList = couponReadMapper.selectCanUseForAccout(req);
            
            return canUsedCouponList;
        }
        catch (Exception e)
        {
            LOGGER.error("canUseAuthCode|查询可用的授权码失败.", e);
            throw LandseaException.createException(AccountResultCode.E00950008);
        }
        
    }
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public void authCodeInto(AuthCodeIntoReq req)
        throws LandseaException
    {
        try
        {
            AccountRecharge accountRecharge = new AccountRecharge();
            BillPayment billPayment = new BillPayment();
            Account account = accountReadMapper.selectByPrimaryKey(req.getAccountId());
            if (null == account)
            {
                throw LandseaException.createException(AccountResultCode.E00950003);
            }
            
            // 如果账户不正常，则不能充值
            if (!AccountStatus.NORMAL.equals(account.getStatus()))
            {
                LOGGER.error("租客账户冻结，不能授权码转入.status=" + account.getStatus());
                throw LandseaException.createException(PaymentResultCode.E00150034);
            }
            
            // 查询余额总数
            Double preAutoTotalLeftMoney = accountBalanceReadMapper.getTotalMoneyByAccountId(account.getId());
            Coupon coupon = couponReadMapper.selectByPrimaryKey(req.getCouponId());
            if (null == coupon)
            {
                throw LandseaException.createException(AuthorizationResultCode.E00550001);
            }
            if (req.getMoney().intValue() > coupon.getQuota().intValue())
            {
                throw LandseaException.createException(AccountResultCode.E009500022);
            }
            else
            {
                // 账单支付表加记录
                billPayment.setBillId(req.getAccountId());
                billPayment.setTime(new Timestamp(new Date().getTime()));
                billPayment.setPayType(PayType.AUTHCODE_ALIPAY);
                billPayment.setMoney(req.getMoney());
                billPayment.setStatus(BillPaymentStatus.SUCCESS);
                billPayment.setBillType(BillType.AUTHCODE_BILL);
                billPayment.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
                billPaymentWriteMapper.insertSelective(billPayment);
                
                // 往余额表中修改余额
                AccountBalance accountBalance = new AccountBalance();
                accountBalance.setAccountId(req.getAccountId());
                accountBalance.setBalanceType(BalanceType.COMMON);
                accountBalance = accountBalanceReadMapper.selectByAccountIdAndType(accountBalance);
                if (null == accountBalance)
                {
                    throw LandseaException.createException(AccountResultCode.E009500021);
                }
                Double currentMoney = accountBalance.getMoney();
                Double freezeMoney = accountBalance.getFreezeMoney();
                // 充值之后总余额
                double afterAutoPayTotalMoney = Arith.add(preAutoTotalLeftMoney, req.getMoney());
                accountBalance.setMoney(Arith.add(req.getMoney(), currentMoney));
                if (!(req.getRefund()))
                {
                    accountBalance.setFreezeMoney(Arith.add(req.getMoney(), freezeMoney));
                }
                accountBalance.editInitCommonField();
                accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalance);
                
                // 充值记录表加记录
                accountRecharge.setAccountId(req.getAccountId());
                accountRecharge.setAccountBalance(Arith.add(req.getMoney(), currentMoney));
                accountRecharge.setBalanceType(BalanceType.COMMON);
                accountRecharge.setScenarioId(req.getCouponId());
                if (!(req.getRefund()))
                {
                    accountRecharge.setComment("优惠转入|" + coupon.getAuthCode() + "(不可退)");
                }
                else
                {
                    accountRecharge.setComment("优惠转入|" + coupon.getAuthCode());
                }
                accountRecharge.setMoney(req.getMoney());
                accountRecharge.setPaymentId(billPayment.getId());
                accountRecharge.setStatus("成功");
                accountRecharge.setTime(new Timestamp(new Date().getTime()));
                accountRecharge.setWid(StringTools.generateUuid());
                accountRecharge.initCommonField();
                accountRechargeWriteMapper.insertSelective(accountRecharge);
                
                // 使用授权码
                // 主表
                coupon.setUsedAmount(1);
                coupon.setStatus(CouponStatus.USED);
                couponWriteMapper.updateByPrimaryKey(coupon);
                // 使用表
                CoupUsedRecord coupUsedRecord = new CoupUsedRecord();
                coupUsedRecord.setAuthFee(coupon.getAuthFee());
                coupUsedRecord.setCouponId(req.getCouponId());
                coupUsedRecord.setMainId(req.getAccountId());
                coupUsedRecord.setMoneyType(MoneyType.MONEY);
                coupUsedRecord.setProjectId(req.getProjectId());
                coupUsedRecord.setRealQuota(req.getMoney());
                coupUsedRecord.setScenarioType(QueryUesdCodeType.TOACCOUNT);
                coupUsedRecord.setUesdStatus(DepositEnum._DS3.getCode());
                coupUsedRecord.setUserId(coupon.getUserId());
                coupUsedRecord.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
                coupUsedRecordWriteMapper.insertSelective(coupUsedRecord);
                
                try
                {
                    autoPayBillService.processMoneyChangeWechatMsg(preAutoTotalLeftMoney,
                        afterAutoPayTotalMoney,
                        account,
                        true);
                }
                catch (Exception e)
                {
                    LOGGER.error("authCodeInto|授权码转入余额，发送微信消息失败.", e);
                }
            }
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("authCodeInto|授权码转入失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500020);
        }
    }
    
    public List<AcotBalaForWCResp> getAccountBalanceForWechat()
        throws LandseaException
    {
        try
        {
            Integer userId = SecurityUtils.getCurrentLogin().getUserId();
            List<AcotBalaForWCResp> resultResps = new ArrayList<AcotBalaForWCResp>();
            List<AcotBalaForWCResp> accountResps = accountReadMapper.getAccountBalanceForWechat(userId);
            if (CollectionUtils.isEmpty(accountResps))
            {
                throw LandseaException.createException(AccountResultCode.E009500010);
            }
            for (AcotBalaForWCResp acotBalaForWCResp : accountResps)
            {
                Contract contract = getContractForWeChat(acotBalaForWCResp.getUserId(), acotBalaForWCResp.getRoomId());
                if (null == contract)
                {
                    continue;
                }
                else
                {
                    acotBalaForWCResp.setContractId(contract.getId());
                    acotBalaForWCResp.setContractStatus(contract.getStatus());
                    List<AccountBalanceResp> accountBalanceResps =
                        accountBalanceReadMapper.selectByAccountId(acotBalaForWCResp.getAccountId());
                    
                    // 对账户余额项进行处理，过滤掉那些没有开放给微信租客看的余额项，该配置在项目管理-微信余额项处配置
                    filterWechatAccountBalance(accountBalanceResps, acotBalaForWCResp.getRoomId());
                    
                    acotBalaForWCResp.setBalanceTypeList(accountBalanceResps);
                    resultResps.add(acotBalaForWCResp);
                }
            }
            return resultResps;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("getAccountBalanceForWechat|微信端查询账户信息失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500013);
        }
        
    }
    
    /**
     * 
     * [简要描述]：微信端要过滤掉不展示的余额费项</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-25)</br>
     *
     * @param accountBalanceResps
     * @param roomId
     *
     */
    private void filterWechatAccountBalance(List<AccountBalanceResp> accountBalanceResps, Integer roomId)
    {
        Project project = projectReadMapper.getByRoomId(roomId);
        
        if (null == project)
        {
            LOGGER.error("filterWechatAccountBalance|项目不存在.RoomId=" + roomId);
            accountBalanceResps.clear();
            return;
        }
        
        String projectAccountCostType = project.getAccountCostType();
        if (StringUtils.isEmpty(projectAccountCostType))
        {
            LOGGER.info("filterWechatAccountBalance|项目配置的账户余额项为空.projectId=" + project.getId());
            accountBalanceResps.clear();
            return;
        }
        
        String[] projectAccountCostTypeArray = projectAccountCostType.split(",");
        List<String> prjAccountCostTypeList = Arrays.asList(projectAccountCostTypeArray);
        
        if (null == accountBalanceResps)
        {
            return;
        }
        
        // 不显示的，要记录下来总金额
        double hiddenTotalMoney = 0.00d;
        
        Iterator<AccountBalanceResp> iterator = accountBalanceResps.iterator();
        while (iterator.hasNext())
        {
            AccountBalanceResp accountBalanceResp = iterator.next();
            
            // 通用的不需要
            if (BalanceType.COMMON.equals(accountBalanceResp.getBalanceTypeCode()))
            {
                accountBalanceResp.setBalanceType("通用");
            }
            
            // 只有在项目配置中的才可以显示
            if (!prjAccountCostTypeList.contains(accountBalanceResp.getBalanceTypeCode()))
            {
                hiddenTotalMoney = Arith.add(accountBalanceResp.getMoney(), hiddenTotalMoney);
                iterator.remove();
            }
        }
        
        if (hiddenTotalMoney > 0.00d)
        {
            AccountBalanceResp accountBalanceResp = new AccountBalanceResp();
            accountBalanceResp.setBalanceType("其他");
            accountBalanceResp.setBalanceTypeCode("other_total_Balance");
            accountBalanceResp.setMoney(hiddenTotalMoney);
            accountBalanceResps.add(accountBalanceResp);
        }
    }
    
    /**
     * 
     * [简要描述]：微信端租客能看到的账户要满足存在以下合同</br>
     * [详细描述]：签约中的或者结束状态，但有未支付完成的账单</br>
     * [作者]：Aaron(2017-12-20)</br>
     *
     * @param userId
     * @param roomId
     *
     */
    private Contract getContractForWeChat(Integer userId, Integer roomId)
    {
        // 先查询除了删除之外所有的合同，查询出来直接是按照ID升序排列
        List<Contract> contracts = contractReadMapper.selectByRmIdAndUrId(userId, roomId);
        if (CollectionUtils.isEmpty(contracts))
        {
            return null;
        }
        
        for (Contract contract : contracts)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            if (ContractStatusCodeEnum.SIGN.getCode().equals(contract.getStatus()))
            {
                return contract;
            }
        }
        
        int size = contracts.size();
        
        // 如果没有签约中的，查看是否有结束的合同
        for (int i = size - 1; i >= 0; i--)
        {
            // 有签约中，直接返回签约中的，此情况包含了续租时存在2份签约中的合同，优先返回A合同
            Contract contract = contracts.get(i);
            if (ContractStatusCodeEnum.END.getCode().equals(contract.getStatus()))
            {
                // 查看是否存在未支付完成的账单
                List<Bill> bills = billReadMapper.queryNoSuccBillForContract(contract.getId());
                if (CollectionUtils.isNotEmpty(bills))
                {
                    return contract;
                }
            }
        }
        
        // 否则返回空
        return null;
    }
    
    public String accountRechargeExcel(QueryUtil queryUtil)
        throws LandseaException
    {
        String url = fileConfiguration.getRemote();
        String excelpath = fileConfiguration.getExcelPath();
        
        InputStream ins = null;
        Workbook workbook = null;
        try
        {
            ins = PrepaymentService.class.getClassLoader().getResourceAsStream("账户充值记录表.xlsx");
            workbook = WorkbookFactory.create(ins);
        }
        catch (Exception e)
        {
            LOGGER.error("accountRechargeExcel|读取Excel模板失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500023, e);
        }
        finally
        {
            try
            {
                if (null != ins)
                {
                    ins.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("accountRechargeExcel|关闭读取流失败.", e);
                throw LandseaException.createException(AccountResultCode.E009500023, e);
            }
        }
        
        List<AccountRechargeResp> list = getAccountRechargeList(queryUtil);
        if (CollectionUtils.isEmpty(list))
        {
            list = new ArrayList<AccountRechargeResp>();
        }
        Map<String, Object> map = new HashMap<String, Object>();
        for (AccountRechargeResp accountRechargeResp : list)
        {
            map = getAccountDetail(accountRechargeResp.getAccountId());
        }
        AccountDetailResp detailResp = (AccountDetailResp)map.get("accountDetail");
        Sheet sheet = workbook.getSheet("账户充值记录表");
        // 生成一个样式
        CellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setAlignment(CellStyle.ALIGN_CENTER);
        // 生成一个字体
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short)14);
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样式
        style.setFont(font);
        
        Row row;
        int startRow = 1;
        for (int i = 0; i < list.size(); i++)
        {
            AccountRechargeResp object = list.get(i);
            
            row = sheet.createRow(startRow + i);
            // 序号
            row.createCell(0).setCellValue(i + 1);
            // 公司
            row.createCell(1).setCellValue(StringTools.getStr(detailResp.getCity()));
            // 门店
            row.createCell(2).setCellValue(StringTools.getStr(detailResp.getProjectName()));
            // 合同编号
            row.createCell(3).setCellValue(StringTools.getStr(detailResp.getContractNo()));
            // 房间号
            row.createCell(4).setCellValue(StringTools.getStr(detailResp.getRoomName()));
            // 客户姓名
            row.createCell(5).setCellValue(StringTools.getStr(detailResp.getUserName()));
            // 余额类型
            row.createCell(6).setCellValue(StringTools.getStr(object.getBalanceType() + "余额"));
            // 当前余额
            row.createCell(7).setCellValue(StringTools.getStr(String.valueOf(object.getAccountBalance())));
            // 充值金额
            row.createCell(8).setCellValue(StringTools.getStr(String.valueOf(object.getMoney())));
            // 充值时间
            row.createCell(9).setCellValue(StringTools.getStr(object.getTime()));
            // 充值方式
            row.createCell(10).setCellValue(StringTools.getStr(object.getPayTypeZh()));
            // 交易号
            row.createCell(11).setCellValue(StringTools.getStr(object.getTransactionNo()));
            // 充值状态
            row.createCell(12).setCellValue(StringTools.getStr(object.getStatus()));
            // 说明
            row.createCell(13).setCellValue(StringTools.getStr(object.getComment()));
        }
        
        // 下面是生成excel文件
        OutputStream fos = null;
        try
        {
            Integer userId = SecurityUtils.getCurrentLogin().getUserId();
            String filepath = fileConfiguration.getLocal() + File.separator + excelpath;
            File file = new File(filepath);
            // 如果文件夹不存在则创建
            if (!file.exists() && !file.isDirectory())
            {
                boolean mkdirResult = file.mkdir();
                if (!mkdirResult)
                {
                    LOGGER.error("accountRechargeExcel|创建临时文件夹失败.filepath=" + filepath);
                    throw LandseaException.createException(AccountResultCode.E009500023);
                }
            }
            
            // 删除userId开头的excel
            String fileExcelPre = "账户充值记录表_" + userId + "_";
            String[] files = file.list(new FilenameFilter()
            {
                @Override
                public boolean accept(File dir, String name)
                {
                    return name.startsWith(fileExcelPre) && name.endsWith(".xlsx");
                }
            });
            if (null != files)
            {
                // 获取7天前的时间戳
                Calendar calendar = Calendar.getInstance();
                calendar.add(Calendar.DAY_OF_YEAR, -7);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                long currentDateLong = calendar.getTimeInMillis();
                
                // 删除修改时间一周之前的文件
                for (String f : files)
                {
                    File fileDelete = new File(filepath + File.separator + f);
                    if (currentDateLong >= fileDelete.lastModified())
                    {
                        fileDelete.delete();
                    }
                }
            }
            
            Date nowTime = new Date();
            String time = DateUtil.convertWeBankTime(nowTime);
            
            String fileName = fileExcelPre + time + ".xlsx";
            fos = new FileOutputStream(filepath + File.separator + fileName);
            workbook.write(fos);
            
            fos.flush();
            
            StringBuffer returnUrl = new StringBuffer();
            returnUrl.append(url);
            returnUrl.append(File.separator);
            returnUrl.append(excelpath);
            returnUrl.append(File.separator);
            returnUrl.append(fileName);
            return returnUrl.toString();
        }
        catch (Exception e)
        {
            LOGGER.error("accountRechargeExcel|生成账户充值excel失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500023);
        }
        finally
        {
            try
            {
                if (null != fos)
                {
                    fos.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("accountRechargeExcel|生成账户充值excel关闭流失败.", e);
                throw LandseaException.createException(AccountResultCode.E009500023);
            }
        }
    }
    
    public String accountChargebackExcel(QueryUtil queryUtil)
        throws LandseaException
    {
        String url = fileConfiguration.getRemote();
        String excelpath = fileConfiguration.getExcelPath();
        
        InputStream ins = null;
        Workbook workbook = null;
        try
        {
            ins = PrepaymentService.class.getClassLoader().getResourceAsStream("账户扣款记录表.xlsx");
            workbook = WorkbookFactory.create(ins);
        }
        catch (Exception e)
        {
            LOGGER.error("accountChargebackExcel|读取Excel模板失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500024, e);
        }
        finally
        {
            try
            {
                if (null != ins)
                {
                    ins.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("accountChargebackExcel|关闭读取流失败.", e);
                throw LandseaException.createException(AccountResultCode.E009500024, e);
            }
        }
        
        List<AccountChargebackResp> list = getAccountChargebackList(queryUtil);
        if (CollectionUtils.isEmpty(list))
        {
            list = new ArrayList<AccountChargebackResp>();
        }
        Map<String, Object> map = new HashMap<String, Object>();
        for (AccountChargebackResp accountChargebackResp : list)
        {
            map = getAccountDetail(accountChargebackResp.getAccountId());
        }
        AccountDetailResp detailResp = (AccountDetailResp)map.get("accountDetail");
        Sheet sheet = workbook.getSheet("账户扣款记录表");
        // 生成一个样式
        CellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setAlignment(CellStyle.ALIGN_CENTER);
        // 生成一个字体
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short)14);
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样式
        style.setFont(font);
        
        Row row;
        int startRow = 1;
        for (int i = 0; i < list.size(); i++)
        {
            AccountChargebackResp object = list.get(i);
            
            row = sheet.createRow(startRow + i);
            // 序号
            row.createCell(0).setCellValue(i + 1);
            // 公司
            row.createCell(1).setCellValue(StringTools.getStr(detailResp.getCity()));
            // 门店
            row.createCell(2).setCellValue(StringTools.getStr(detailResp.getProjectName()));
            // 合同编号
            row.createCell(3).setCellValue(StringTools.getStr(detailResp.getContractNo()));
            // 房间号
            row.createCell(4).setCellValue(StringTools.getStr(detailResp.getRoomName()));
            // 客户姓名
            row.createCell(5).setCellValue(StringTools.getStr(detailResp.getUserName()));
            // 余额类型
            row.createCell(6).setCellValue(StringTools.getStr(object.getBalanceType() + "余额"));
            // 抵扣费项
            row.createCell(7).setCellValue(StringTools.getStr(object.getFeeType()));
            // 说明
            row.createCell(8).setCellValue(StringTools.getStr(object.getRemark()));
            // 抵扣金额
            row.createCell(9).setCellValue(StringTools.getStr(String.valueOf(object.getMoney())));
            // 抵抗时间
            row.createCell(10).setCellValue(StringTools.getStr(object.getTime()));
        }
        
        // 下面是生成excel文件
        OutputStream fos = null;
        try
        {
            Integer userId = SecurityUtils.getCurrentLogin().getUserId();
            String filepath = fileConfiguration.getLocal() + File.separator + excelpath;
            File file = new File(filepath);
            // 如果文件夹不存在则创建
            if (!file.exists() && !file.isDirectory())
            {
                boolean mkdirResult = file.mkdir();
                if (!mkdirResult)
                {
                    LOGGER.error("accountChargebackExcel|创建临时文件夹失败.filepath=" + filepath);
                    throw LandseaException.createException(AccountResultCode.E009500024);
                }
            }
            
            // 删除userId开头的excel
            String fileExcelPre = "账户扣款记录表_" + userId + "_";
            String[] files = file.list(new FilenameFilter()
            {
                @Override
                public boolean accept(File dir, String name)
                {
                    return name.startsWith(fileExcelPre) && name.endsWith(".xlsx");
                }
            });
            if (null != files)
            {
                // 获取7天前的时间戳
                Calendar calendar = Calendar.getInstance();
                calendar.add(Calendar.DAY_OF_YEAR, -7);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                long currentDateLong = calendar.getTimeInMillis();
                
                // 删除修改时间一周之前的文件
                for (String f : files)
                {
                    File fileDelete = new File(filepath + File.separator + f);
                    if (currentDateLong >= fileDelete.lastModified())
                    {
                        fileDelete.delete();
                    }
                }
            }
            
            Date nowTime = new Date();
            String time = DateUtil.convertWeBankTime(nowTime);
            
            String fileName = fileExcelPre + time + ".xlsx";
            fos = new FileOutputStream(filepath + File.separator + fileName);
            workbook.write(fos);
            
            fos.flush();
            
            StringBuffer returnUrl = new StringBuffer();
            returnUrl.append(url);
            returnUrl.append(File.separator);
            returnUrl.append(excelpath);
            returnUrl.append(File.separator);
            returnUrl.append(fileName);
            return returnUrl.toString();
        }
        catch (Exception e)
        {
            LOGGER.error("accountChargebackExcel|生成账户扣款excel失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500024);
        }
        finally
        {
            try
            {
                if (null != fos)
                {
                    fos.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("accountChargebackExcel|生成账户扣款excel关闭流失败.", e);
                throw LandseaException.createException(AccountResultCode.E009500024);
            }
        }
    }
    
    /**
     * 
     * [简要描述]：查询账户总金额</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-07)</br>
     *
     * @param userId
     * @param roomId
     * @return
     *
     */
    public AccountResp getAccountTotalMoney(Integer userId, Integer roomId)
    {
        Account account = accountReadMapper.selectAccount(roomId, userId);
        if (null == account)
        {
            return null;
        }
        
        // 查询总余额
        Double totalMoney = accountBalanceReadMapper.getTotalMoneyByAccountId(account.getId());
        AccountResp result = new AccountResp();
        result.setId(account.getId());
        result.setMoney(totalMoney);
        
        return result;
    }
    
    /**
     * 
     * [简要描述]：退租时处理余额的结算</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-08)</br>
     *
     * @param preOffrentDetailBills
     * @param contract
     *
     */
    public void offrentProcessAccount(List<PreOffrentDetailBill> preOffrentDetailBills, Contract contract)
    {
        if (null == contract)
        {
            return;
        }
        LOGGER.info("offrentProcessAccount|开始处理余额生成退租结算单.合同ID=" + contract.getId());
        
        // 查询账户下所有余额项
        List<AccountBalance> accountBalaceList = accountBalanceReadMapper.selectListByContract(contract);
        if (CollectionUtils.isEmpty(accountBalaceList))
        {
            LOGGER.info("offrentProcessAccount|无余额项可结算.");
            return;
        }
        
        // 所有冻结的金额
        double totalFrozenMoney = 0.00d;
        // 所有余额
        double totalMoney = 0.00d;
        Integer accountId = accountBalaceList.get(0).getAccountId();
        for (AccountBalance accountBalance : accountBalaceList)
        {
            totalFrozenMoney = Arith.add(accountBalance.getFreezeMoney(), totalFrozenMoney);
            totalMoney = Arith.add(accountBalance.getMoney(), totalMoney);
        }
        
        double totalBackMoney = Arith.sub(totalMoney, totalFrozenMoney);
        
        StringBuilder sbBuilder = new StringBuilder();
        sbBuilder.append("offrentProcessAccount|账户ID=");
        sbBuilder.append(accountId);
        sbBuilder.append("的账户,总余额");
        sbBuilder.append(totalMoney);
        sbBuilder.append("其中冻结金额");
        sbBuilder.append(totalFrozenMoney);
        sbBuilder.append(",可结余");
        sbBuilder.append(totalBackMoney);
        
        LOGGER.info(sbBuilder.toString());
        
        if (totalBackMoney > 0)
        {
            totalBackMoney = -totalBackMoney;
        }
        
        // 不为0时才需要体现在结算单
        if (totalBackMoney != 0.00d)
        {
            PreOffrentDetailBill balanceOffrentDetail = new PreOffrentDetailBill();
            balanceOffrentDetail.setPreType(BillTypeEnum.CTBI22.getCode());
            balanceOffrentDetail.setBillAmount(totalBackMoney);
            balanceOffrentDetail.setDateBegin(contract.getInDate());
            balanceOffrentDetail.setDateEnd(new Date());
            preOffrentDetailBills.add(balanceOffrentDetail);
        }
        
        // 修改账户表，已在退租或转租流程中，无法充值、抵扣
        if (null != contract.getRentType() && ReRentType.CHANGE.equals(contract.getRentType()))
        {
            // 转租
            updateAccountStatusById(accountId, AccountStatus.CHANGE_RENT);
        }
        else
        {
            // 退房
            updateAccountStatusById(accountId, AccountStatus.OFFRENT);
        }
        
        LOGGER.info("已更新账户ID=" + accountId + "的账户状态为：退租中，该账户将不能充值、抵扣");
    }
    
    public void updateAccountStatusById(Integer accountId, String status)
    {
        Account updateAccount = new Account();
        updateAccount.setId(accountId);
        updateAccount.setStatus(status);
        updateAccount.editInitCommonField();
        accountWriteMapper.updateByPrimaryKeySelective(updateAccount);
    }
    
    public void updateAccountStatusByRoomAndUser(Integer roomId, Integer userId, String status)
    {
        Account updateAccount = new Account();
        updateAccount.setUserId(userId);
        updateAccount.setRoomId(roomId);
        updateAccount.setStatus(status);
        updateAccount.editInitCommonField();
        accountWriteMapper.updateAccountStatusByRoomAndUser(updateAccount);
    }
    
    public void updateAccountStatusByContract(Integer contractId, String status)
    {
        Contract contract = contractReadMapper.selectByPrimaryKey(contractId);
        if (null == contract)
        {
            LOGGER.error("updateAccountStatusByContract|更新账户状态时合同不存在contractId=" + contractId);
            return;
        }
        updateAccountStatusByRoomAndUser(contract.getRoomId(), contract.getUserId(), status);
    }
    
    /**
     * 
     * [简要描述]：项目费项编辑后，处理费项</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-22)</br>
     *
     * @param prjId
     * @param costType
     * @throws LandseaException
     *
     */
    public void editBalanceType(Integer prjId, String costType)
        throws LandseaException
    {
        try
        {
            LOGGER.info("editBalanceType|项目费项有改动,开始处理账户余额项.projectId=" + prjId);
            
            // 新的费项
            List<String> newBalanceTypeList = new ArrayList<String>();
            newBalanceTypeList.add(BalanceType.COMMON);
            newBalanceTypeList.add(BillTypeEnum.CTBI11.getCode());
            newBalanceTypeList.add(BillTypeEnum.CTBI10.getCode());
            newBalanceTypeList.add(BillTypeEnum.CTBI8.getCode());
            newBalanceTypeList.add(BillTypeEnum.CTBI9.getCode());
            String[] arrs = costType.split(",");
            for (String string : arrs)
            {
                // 过滤掉实时,“其他”“账户余额”
                if ("_".equals(string.subSequence(0, 1)))
                {
                    if (newBalanceTypeList.contains(string))
                    {
                        continue;
                    }
                    if (BillTypeEnum.CTBI12.getCode().equals(string) || BillTypeEnum.CTBI22.getCode().equals(string))
                    {
                        continue;
                    }
                    newBalanceTypeList.add(string);
                }
            }
            
            // 查询该项目下所有的账户
            List<Account> accounts = accountReadMapper.selectByPrjId(prjId);
            if (CollectionUtils.isEmpty(accounts))
            {
                LOGGER.info("editBalanceType|项目下无账户，处理结束.");
                return;
            }
            
            // 随便查询一个账户下所有的余额项，检查本次项目费项改动是新增了费项还是
            List<AccountBalanceResp> accountBalanceResps =
                accountBalanceReadMapper.selectByAccountId(accounts.get(0).getId());
            
            if (null == accountBalanceResps)
            {
                accountBalanceResps = new ArrayList<AccountBalanceResp>();
            }
            
            List<String> balanceTypeList = new ArrayList<String>();
            for (AccountBalanceResp accountBalance : accountBalanceResps)
            {
                balanceTypeList.add(accountBalance.getBalanceTypeCode());
            }
            // 比较且剔除都存在的项
            LandSeaUtil.getStrListDiffrent(newBalanceTypeList, balanceTypeList);
            
            // 通过以上处理
            // 如果新增列表还有剩余，说明是新增加了费项
            if (newBalanceTypeList.size() > 0)
            {
                LOGGER.info("editBalanceType|增加费项" + newBalanceTypeList);
                
                // 循环新增每一个账户
                for (Account account : accounts)
                {
                    for (String addBalanceType : newBalanceTypeList)
                    {
                        AccountBalance saveAccountBalance = new AccountBalance();
                        saveAccountBalance.setWid(StringTools.generateUuid());
                        saveAccountBalance.setAccountId(account.getId());
                        saveAccountBalance.setBalanceType(addBalanceType);
                        saveAccountBalance.setMoney(0.00d);
                        saveAccountBalance.setFreezeMoney(0.00d);
                        saveAccountBalance.initCommonField();
                        accountBalanceWriteMapper.insertSelective(saveAccountBalance);
                    }
                }
            }
            // 如果账户列表还有剩余，那么是删除了费项
            if (balanceTypeList.size() > 0)
            {
                LOGGER.info("editBalanceType|减少费项" + balanceTypeList);
                // 循环每一个账户，进行删除
                for (Account account : accounts)
                {
                    deleteBalance4UpdateFee(account, balanceTypeList);
                }
            }
        }
        catch (Exception e)
        {
            LOGGER.error("editBalanceType|项目编辑修改费项失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500025);
        }
    }
    
    /**
     * 
     * [简要描述]：项目费项减少更新账户</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-22)</br>
     *
     * @param account
     * @param balanceTypeList
     *
     */
    private void deleteBalance4UpdateFee(Account account, List<String> balanceTypeList)
    {
        LOGGER.info("deleteBalance4UpdateFee|开始处理余额项结转.AccountId=" + account.getId());
        
        // 先查询账户下所有的余额项
        List<AccountBalanceResp> currentBalanceList = accountBalanceReadMapper.selectByAccountId(account.getId());
        
        if (CollectionUtils.isEmpty(currentBalanceList))
        {
            LOGGER.info("deleteBalance4UpdateFee|账户不存在余额项.AccountId=" + account.getId());
            return;
        }
        
        // 拿到通用
        AccountBalanceResp commonBalnace = null;
        for (AccountBalanceResp balance : currentBalanceList)
        {
            if (LandeaConstants.BalanceType.COMMON.equals(balance.getBalanceTypeCode()))
            {
                commonBalnace = balance;
                break;
            }
        }
        
        if (null == commonBalnace)
        {
            LOGGER.error("deleteBalance4UpdateFee|账户不存在通用余额项,不允许删除费项.AccountId=" + account.getId());
            return;
        }
        
        Date nowDate = new Date();
        
        for (String deleteBalanceType : balanceTypeList)
        {
            for (AccountBalanceResp balance : currentBalanceList)
            {
                // 过滤出要删除的
                if (deleteBalanceType.equals(balance.getBalanceTypeCode()))
                {
                    // 如果余额为0且冻结金额为0，则不处理
                    if (balance.getMoney().doubleValue() == 0.00d && balance.getFreezeMoney().doubleValue() == 0.00d)
                    {
                        LOGGER.info("deleteBalance4UpdateFee|账户" + balance.getBalanceType() + "和冻结金额为0无需结转,AccountId="
                            + account.getId());
                        continue;
                    }
                    
                    if (balance.getMoney().doubleValue() == 0.00d)
                    {
                        LOGGER.info("deleteBalance4UpdateFee|账户" + balance.getBalanceType() + "为0,只结转冻结金额,AccountId="
                            + account.getId());
                        // 余额为0，冻结金额不为0，只更新冻结金额
                        AccountBalance deleteAccountBalance = new AccountBalance();
                        deleteAccountBalance.setId(balance.getAccountBalanceId());
                        deleteAccountBalance.editInitCommonField();
                        accountBalanceWriteMapper.deleteByBalanceId(deleteAccountBalance);
                        commonBalnace
                            .setFreezeMoney(Arith.add(commonBalnace.getFreezeMoney(), balance.getFreezeMoney()));
                        AccountBalance updateCommonMoney = new AccountBalance();
                        updateCommonMoney.setId(commonBalnace.getAccountBalanceId());
                        updateCommonMoney.setFreezeMoney(commonBalnace.getFreezeMoney());
                        updateCommonMoney.editInitCommonField();
                        accountBalanceWriteMapper.updateByPrimaryKeySelective(updateCommonMoney);
                        
                        continue;
                    }
                    
                    // 以下是处理余额和冻结金额均不为0的情况
                    // 软删除
                    AccountBalance deleteAccountBalance = new AccountBalance();
                    deleteAccountBalance.setId(balance.getAccountBalanceId());
                    deleteAccountBalance.editInitCommonField();
                    accountBalanceWriteMapper.deleteByBalanceId(deleteAccountBalance);
                    // 将余额转入通用
                    commonBalnace.setMoney(Arith.add(commonBalnace.getMoney(), balance.getMoney()));
                    commonBalnace.setFreezeMoney(Arith.add(commonBalnace.getFreezeMoney(), balance.getFreezeMoney()));
                    AccountBalance updateCommonMoney = new AccountBalance();
                    updateCommonMoney.setId(commonBalnace.getAccountBalanceId());
                    updateCommonMoney.setMoney(commonBalnace.getMoney());
                    updateCommonMoney.setFreezeMoney(commonBalnace.getFreezeMoney());
                    updateCommonMoney.editInitCommonField();
                    accountBalanceWriteMapper.updateByPrimaryKeySelective(updateCommonMoney);
                    
                    // 生成支付记录
                    BillPayment insertBillPayment = new BillPayment();
                    insertBillPayment.setMoney(balance.getMoney());
                    insertBillPayment.setBillId(commonBalnace.getAccountId());
                    insertBillPayment.setBillType(BillTypeCode.RECHARGE);
                    insertBillPayment.setPayType(PayType.BALANCE_AUTOPAY);
                    insertBillPayment.setTime(nowDate);
                    // 直接就是支付成功的
                    insertBillPayment.setStatus(BillPaymentStatus.SUCCESS);
                    insertBillPayment.setComments("项目费项减少余额项结转");
                    insertBillPayment.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
                    billPaymentWriteMapper.insertSelective(insertBillPayment);
                    
                    // 增加充值记录
                    AccountRecharge accountRecharge = new AccountRecharge();
                    accountRecharge.setWid(StringTools.generateUuid());
                    accountRecharge.setAccountId(commonBalnace.getAccountId());
                    accountRecharge.setBalanceType(commonBalnace.getBalanceTypeCode());
                    accountRecharge.setMoney(balance.getMoney());
                    accountRecharge.setAccountBalance(commonBalnace.getMoney());
                    accountRecharge.setTime(nowDate);
                    accountRecharge.setPaymentId(insertBillPayment.getId());
                    accountRecharge.setComment("项目费项减少余额项结转");
                    accountRecharge.setStatus("成功");
                    accountRecharge.initCommonField();
                    accountRechargeWriteMapper.insertSelective(accountRecharge);
                    
                    // 增加扣款记录
                    AccountChargeback accountChargeback = new AccountChargeback();
                    accountChargeback.setAccountId(commonBalnace.getAccountId());
                    accountChargeback.setBalanceType(balance.getBalanceTypeCode());
                    accountChargeback.setFeeType(commonBalnace.getBalanceTypeCode());
                    accountChargeback.setMoney(balance.getMoney());
                    accountChargeback.setRemark("项目费项减少余额项结转");
                    accountChargeback.setTime(nowDate);
                    accountChargeback.setLeftMoney(0.00d);
                    accountChargeback.setWid(StringTools.generateUuid());
                    accountChargeback.initCommonField();
                    accountChargebackWriteMapper.insertSelective(accountChargeback);
                }
            }
        }
    }
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public void createAccountOfEnd()
        throws LandseaException
    {
        try
        {
            List<Contract> contracts = contractReadMapper.selectContractByStatus(ContractStatusCodeEnum.END.getCode());
            if (CollectionUtils.isNotEmpty(contracts))
            {
                for (Contract contract : contracts)
                {
                    Account previousAccount =
                        accountReadMapper.selectAccount(contract.getRoomId(), contract.getUserId());
                    if (null == previousAccount)
                    {
                        LOGGER.info("createAccountOfSign|合同ID=" + contract.getId() + "开始创建账户.");
                        
                        // 创建账户
                        Account saveAccount = new Account();
                        saveAccount.setProjectId(contract.getProjectId());
                        saveAccount.setRoomId(contract.getRoomId());
                        saveAccount.setUserId(contract.getUserId());
                        saveAccount.setWid(StringTools.generateUuid());
                        saveAccount.initCommonField();
                        
                        // 创建账户余额表
                        addAccountBalance(saveAccount.getId(), contract.getProjectId());
                    }
                }
            }
        }
        catch (Exception e)
        {
            LOGGER.error("createAccountOfEnd|生成已结束合同的账户.", e);
            throw LandseaException.createException(AccountResultCode.E009500026);
        }
        
    }
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public void createAccountOfSign()
        throws LandseaException
    {
        try
        {
            List<Contract> contracts = contractReadMapper.selectContractByStatus(ContractStatusCodeEnum.SIGN.getCode());
            if (CollectionUtils.isNotEmpty(contracts))
            {
                for (Contract contract : contracts)
                {
                    
                    Account previousAccount =
                        accountReadMapper.selectAccount(contract.getRoomId(), contract.getUserId());
                    if (null == previousAccount)
                    {
                        LOGGER.info("createAccountOfSign|合同ID=" + contract.getId() + "开始创建账户.");
                        
                        // 创建账户
                        Account saveAccount = new Account();
                        saveAccount.setProjectId(contract.getProjectId());
                        saveAccount.setRoomId(contract.getRoomId());
                        saveAccount.setUserId(contract.getUserId());
                        saveAccount.setWid(StringTools.generateUuid());
                        saveAccount.initCommonField();
                        accountWriteMapper.insertSelective(saveAccount);
                        // 创建账户余额表
                        addAccountBalance(saveAccount.getId(), contract.getProjectId());
                    }
                }
            }
        }
        catch (Exception e)
        {
            LOGGER.error("createAccountOfSign|生成签约中合同的账户失败.", e);
            throw LandseaException.createException(AccountResultCode.E009500027);
        }
    }
    
    /**
     * 
     * [简要描述]：续租余额结转</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-08)</br>
     *
     * @param userId
     * @param roomId
     * @param money
     * @param payDate
     * @throws LandseaException
     *
     */
    public void addAccountBalanceMoney4Rerent(Integer userId, Integer roomId, double money, Date payDate)
        throws LandseaException
    {
        Account account = accountReadMapper.selectAccount(roomId, userId);
        if (null == account)
        {
            LOGGER.error("addAccountBalanceMoney4Rerent|账户不存在.userId=" + userId + ",roomId=" + roomId);
            throw LandseaException.createException(PaymentResultCode.E00140010);
        }
        
        Integer accountId = account.getId();
        
        // 查询指定余额项
        AccountBalance accountBalance = new AccountBalance();
        accountBalance.setAccountId(accountId);
        accountBalance.setBalanceType(BalanceType.COMMON);
        accountBalance = accountBalanceReadMapper.selectByAccountIdAndType(accountBalance);
        if (null == accountBalance)
        {
            LOGGER.error("addAccountBalanceMoney4Rerent|账户余额项不存在.accountId=" + accountId);
            throw LandseaException.createException(PaymentResultCode.E00140010);
        }
        
        double totalMoney = Arith.add(money, accountBalance.getMoney());
        AccountBalance accountBalanceUpdate = new AccountBalance();
        accountBalanceUpdate.setId(accountBalance.getId());
        accountBalanceUpdate.editInitCommonField();
        accountBalanceUpdate.setMoney(totalMoney);
        accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
        
        // 生成支付记录
        BillPayment insertBillPayment = new BillPayment();
        insertBillPayment.setMoney(money);
        insertBillPayment.setBillId(accountId);
        insertBillPayment.setBillType(BillTypeCode.RECHARGE);
        insertBillPayment.setPayType(PayType.BALANCE_AUTOPAY);
        insertBillPayment.setTime(payDate);
        // 直接就是支付成功的
        insertBillPayment.setStatus(BillPaymentStatus.SUCCESS);
        insertBillPayment.setComments("续租余额结转");
        insertBillPayment.updateCommonInfo(-1);
        billPaymentWriteMapper.insertSelective(insertBillPayment);
        
        // 支付成功，添加充值记录
        AccountRecharge accountRecharge = new AccountRecharge();
        accountRecharge.setWid(StringTools.generateUuid());
        accountRecharge.setAccountId(accountBalance.getAccountId());
        accountRecharge.setBalanceType(accountBalance.getBalanceType());
        accountRecharge.setMoney(money);
        accountRecharge.setAccountBalance(totalMoney);
        accountRecharge.setTime(payDate);
        accountRecharge.setPaymentId(insertBillPayment.getId());
        accountRecharge.setAccountBalance(totalMoney);
        accountRecharge.setComment("续租余额结转");
        accountRecharge.setStatus("成功");
        accountRecharge.initCommonField();
        accountRechargeWriteMapper.insertSelective(accountRecharge);
        LOGGER.info("addAccountBalanceMoney4Rerent|向账户ID=" + accountId + "通用余额中结转" + money);
    }
    
    /**
     * 
     * [简要描述]：退租余额归零0账户</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-11)</br>
     *
     * @param userId
     * @param roomId
     * @throws LandseaException
     *
     */
    public void offRentclearAccount(Integer userId, Integer roomId)
        throws LandseaException
    {
        try
        {
            Account account = accountReadMapper.selectAccount(roomId, userId);
            if (null == account)
            {
                LOGGER.error("offRentclearAccount|退租生成账单,账户不存在,处理退出.userId=" + userId + ",roomId=" + roomId);
                return;
            }
            Integer accountId = account.getId();

            LOGGER.info("offRentclearAccount|账户状态为" + account.getStatus() + ".AccountId=" + accountId);
            
            LOGGER.info("offRentclearAccount|退租或转租生成账单,清空账户.AccountId=" + accountId);
            
            // 只有退租中的账户需要清空
            if (!(AccountStatus.OFFRENT.equals(account.getStatus())
                || AccountStatus.CHANGE_RENT.equals(account.getStatus())))
            {
                LOGGER.info("offRentclearAccount|账户不是处于退租或转租状态中,无需清空余额.AccountId=" + accountId);
                return;
            }
            
            // 是否是转租
            boolean isChangeRent = AccountStatus.CHANGE_RENT.equals(account.getStatus());
            
            List<AccountBalance> balanceList = accountBalanceReadMapper.selectBalanceByAccountId(accountId);
            
            if (CollectionUtils.isEmpty(balanceList))
            {
                LOGGER.info("offRentclearAccount|账户无余额项，清退结束.AccountId=" + accountId);
                return;
            }
            
            Date nowDate = new Date();
            for (AccountBalance accountBalance : balanceList)
            {
                double frozenMoney = accountBalance.getFreezeMoney();
                double money = accountBalance.getMoney();
                
                if (money <= 0.00d && frozenMoney <= 0.00d)
                {
                    continue;
                }
                LOGGER.info("offRentclearAccount|退租清空余额，费项=" + accountBalance.getBalanceType() + ",余额" + money + ",冻结"
                    + frozenMoney);
                
                AccountBalance accountBalanceUpdate = new AccountBalance();
                accountBalanceUpdate.setId(accountBalance.getId());
                accountBalanceUpdate.editInitCommonField();
                
                // 转租不处理冻结金额
                if (isChangeRent)
                {
                    // 如果有冻结金额
                    money = frozenMoney > 0 ? Arith.sub(money, frozenMoney) : money;
                    // 如果最终为负数了，就什么都不干
                    if (money <= 0)
                    {
                        continue;
                    }
                    // 剩下的钱
                    double leftMoney = Arith.sub(accountBalance.getMoney(), money);
                    accountBalanceUpdate.setMoney(leftMoney);
                    accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
                    
                    // 生成扣款记录
                    AccountChargeback accountChargeback = new AccountChargeback();
                    accountChargeback.setAccountId(accountBalance.getAccountId());
                    accountChargeback.setBalanceType(accountBalance.getBalanceType());
                    accountChargeback.setFeeType(accountBalance.getBalanceType());
                    
                    accountChargeback.setMoney(money);
                    accountChargeback.setRemark("转租余额清退");
                    accountChargeback.setTime(nowDate);
                    accountChargeback.setLeftMoney(leftMoney);
                    accountChargeback.setWid(StringTools.generateUuid());
                    accountChargeback.initCommonField();
                    accountChargebackWriteMapper.insertSelective(accountChargeback);
                }
                else
                {
                    accountBalanceUpdate.setMoney(0.00d);
                    accountBalanceUpdate.setFreezeMoney(0.00d);
                    accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
                    
                    // 生成扣款记录
                    AccountChargeback accountChargeback = new AccountChargeback();
                    accountChargeback.setAccountId(accountBalance.getAccountId());
                    accountChargeback.setBalanceType(accountBalance.getBalanceType());
                    accountChargeback.setFeeType(accountBalance.getBalanceType());
                    
                    // 如果是冻结金额，则不要考虑进去
                    money = money >= frozenMoney ? Arith.sub(money, frozenMoney) : 0.00d;
                    accountChargeback.setMoney(money);
                    accountChargeback.setRemark("退房余额清退");
                    accountChargeback.setTime(nowDate);
                    accountChargeback.setLeftMoney(0.00d);
                    accountChargeback.setWid(StringTools.generateUuid());
                    accountChargeback.initCommonField();
                    accountChargebackWriteMapper.insertSelective(accountChargeback);
                }

            }
            LOGGER.info("offRentclearAccount|退租或转租余额归零结束.AccountId=" + accountId);
        }
        catch (Exception e)
        {
            LOGGER.error("offRentclearAccount|退租或转租清算账户余额发生失败.userId=" + userId + ",roomId=" + roomId);
            throw LandseaException.createException(PaymentResultCode.E00150035, e);
        }
    }
    
    /**
     * 
     * [简要描述]：换房处理余额从A结转到B的通用</br>
     * [详细描述]：此处只能抛出LandseaException</br>
     * [作者]：Aaron(2017-12-15)</br>
     *
     * @param contractB B合同
     * @param contractAId A合同ID
     *
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public void changeRoomAccountProcess(Contract contractB, Integer contractAId)
        throws LandseaException
    {
        try
        {
            LOGGER.info(
                "changeRoomAccountProcess|开始处理换房余额结转.contractAId=" + contractAId + ",contractBId=" + contractB.getId());
            
            // 查询A合同
            Contract contractA = contractReadMapper.selectByPrimaryKey(contractAId);
            if (null == contractA || contractA.getDeleted())
            {
                LOGGER.error("changeRoomAccountProcess|换房余额处理错误，A合同不存在.contractAId=" + contractAId);
                return;
            }
            
            Date nowDate = new Date();
            
            // 更新A退房账单为支付成功
            // 查询A合同的退租账单
            Bill billA = billReadMapper.queryOffrentBillByContract(contractA.getId());
            if (null == billA)
            {
                LOGGER.info("changeRoomAccountProcess|A合同无退房账单，处理结束.");
                return;
            }
            
            // 如果A退租账单未支付，则更新成已支付
            if (!BillStatusEnum.BIST2.getCode().equals(billA.getBillStatus()))
            {
                Bill billUpdate = new Bill();
                billUpdate.setId(billA.getId());
                billUpdate.setBillStatus(BillStatusEnum.BIST2.getCode());
                billUpdate.setAmountReceived(billA.getReceivableMoney());
                billUpdate.setLastModifiedDtm(new Timestamp(nowDate.getTime()));
                billUpdate.setLastModifiedBy(SecurityUtils.getCurrentLogin().getUserId());
                billWriteMapper.updateByPrimaryKeySelective(billUpdate);
                // 更新独立账单
                try
                {
                    billIndependentService.monUpdateBalanceSheetRent(contractA.getId(), nowDate);
                }
                catch (LandseaException e)
                {
                    LOGGER.error("changeRoomAccountProcess|更新A退房账单独立账单失败." + e.getErrorDesc());
                }
                LOGGER.info("changeRoomAccountProcess|更新A退房账单支付成功完毕.");
            }
            
            // 查询A账户余额
            List<AccountBalance> accountABalanceList = accountBalanceReadMapper.selectListByContract(contractA);
            if (CollectionUtils.isEmpty(accountABalanceList))
            {
                LOGGER.info("changeRoomAccountProcess|换房余额处理A账户无余额.contractAId=" + contractAId);
                return;
            }
            
            // 获取B账户
            // 先查询数据库，如果没有，查询线程及变量
            List<AccountBalance> accountBalanceBList = accountBalanceReadMapper.selectListByContract(contractB);
            if (CollectionUtils.isEmpty(accountBalanceBList))
            {
                CurrentAccount currentAccount = (CurrentAccount)CurrentThread.get("CURRENTACCOUNT");
                if (null == currentAccount || null == currentAccount.getBalanceList()
                    || currentAccount.getBalanceList().size() == 0)
                {
                    LOGGER.error("changeRoomAccountProcess|账户B不存在，无法结转.roomId=" + contractB.getRoomId() + ",userId="
                        + contractB.getUserId());
                    return;
                }
                accountBalanceBList = currentAccount.getBalanceList();
            }
            
            // 先把B账户所有的费项放入Map
            Map<String, AccountBalance> balanceBMap = new HashMap<String, AccountBalance>();
            for (AccountBalance balanceB : accountBalanceBList)
            {
                balanceBMap.put(balanceB.getBalanceType(), balanceB);
            }
            
            // 计算A账户余额，如果有冻结金额，直接拿过来
            
            // 结转总金额
            double totalTransferMoney = 0.00d;
            
            // 结转总的冻结金额
            double totalFronzenMoney = 0.00d;
            
            for (AccountBalance accountBalance : accountABalanceList)
            {
                if (accountBalance.getMoney() <= 0.00d)
                {
                    LOGGER.info(
                        "changeRoomAccountProcess|A账户" + BillTypeZhUtil.getZhByCode(accountBalance.getBalanceType())
                            + "余额为" + accountBalance.getMoney() + ",无需结转.");
                    continue;
                }
                
                // 计算总金额
                totalTransferMoney = Arith.add(accountBalance.getMoney(), totalTransferMoney);
                totalFronzenMoney = Arith.add(accountBalance.getFreezeMoney(), totalFronzenMoney);
                
                // 当前要结转的费项
                double currentMoney = accountBalance.getMoney();
                double currentForenMoney = accountBalance.getFreezeMoney();
                
                // 获取B账户对应费项
                AccountBalance currentBalanceB = balanceBMap.get(accountBalance.getBalanceType());
                if (null == currentBalanceB)
                {
                    // 如果对应费项不存在，获取通用费项
                    currentBalanceB = balanceBMap.get(BalanceType.COMMON);
                    if (null == currentBalanceB)
                    {
                        // 如果通用也没有，直接报错
                        LOGGER.error(
                            "changeRoomAccountProcess|结转" + BillTypeZhUtil.getZhByCode(accountBalance.getBalanceType())
                                + accountBalance.getMoney() + "到B账户通用错误，B账户通用余额项不存在！");
                        throw LandseaException.createException(PaymentResultCode.E00150036);
                    }
                }
                
                // 清空A账户
                AccountBalance accountBalanceUpdate = new AccountBalance();
                accountBalanceUpdate.setId(accountBalance.getId());
                accountBalanceUpdate.setMoney(0.00d);
                accountBalanceUpdate.setFreezeMoney(0.00d);
                accountBalanceUpdate.editInitCommonField();
                accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
                
                // 新增A的扣款记录
                AccountChargeback accountChargeback = new AccountChargeback();
                accountChargeback.setAccountId(accountBalance.getAccountId());
                accountChargeback.setBalanceType(accountBalance.getBalanceType());
                accountChargeback.setFeeType(accountBalance.getBalanceType());
                accountChargeback.setMoney(accountBalance.getMoney());
                accountChargeback.setRemark("换房余额结转");
                accountChargeback.setTime(nowDate);
                accountChargeback.setLeftMoney(0.00d);
                accountChargeback.setWid(StringTools.generateUuid());
                accountChargeback.initCommonField();
                accountChargebackWriteMapper.insertSelective(accountChargeback);
                
                // 更新B账户余额及冻结金额
                AccountBalance accountBalanceUpdateB = new AccountBalance();
                Double totalMoneyB = Arith.add(currentBalanceB.getMoney(), currentMoney);
                accountBalanceUpdateB.setId(currentBalanceB.getId());
                accountBalanceUpdateB.setMoney(totalMoneyB);
                accountBalanceUpdateB.setFreezeMoney(Arith.add(currentBalanceB.getFreezeMoney(), currentForenMoney));
                accountBalanceUpdateB.editInitCommonField();
                // 更新B账户的通用余额，将钱全转进通用去
                accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdateB);
                
                // 新增B的充值记录
                // 生成支付记录
                BillPayment insertBillPayment = new BillPayment();
                insertBillPayment.setMoney(totalTransferMoney);
                insertBillPayment.setBillId(currentBalanceB.getAccountId());
                insertBillPayment.setBillType(BillTypeCode.RECHARGE);
                insertBillPayment.setPayType(PayType.BALANCE_AUTOPAY);
                insertBillPayment.setTime(nowDate);
                // 直接就是支付成功的
                insertBillPayment.setStatus(BillPaymentStatus.SUCCESS);
                insertBillPayment.setComments("换房余额结转");
                insertBillPayment.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
                billPaymentWriteMapper.insertSelective(insertBillPayment);
                
                // 支付成功，添加B充值记录
                AccountRecharge accountRecharge = new AccountRecharge();
                accountRecharge.setWid(StringTools.generateUuid());
                accountRecharge.setAccountId(currentBalanceB.getAccountId());
                accountRecharge.setBalanceType(currentBalanceB.getBalanceType());
                accountRecharge.setAccountBalance(totalMoneyB);
                accountRecharge.setMoney(currentMoney);
                accountRecharge.setTime(nowDate);
                accountRecharge.setPaymentId(insertBillPayment.getId());
                accountRecharge.setComment("换房余额结转");
                accountRecharge.setStatus("成功");
                accountRecharge.initCommonField();
                accountRechargeWriteMapper.insertSelective(accountRecharge);
                
                LOGGER.info("changeRoomAccountProcess|结转" + BillTypeZhUtil.getZhByCode(accountBalance.getBalanceType())
                    + accountBalance.getMoney() + "到B账户" + BillTypeZhUtil.getZhByCode(currentBalanceB.getBalanceType())
                    + ",其中冻结金额为" + accountBalance.getFreezeMoney());
            }
            
            LOGGER.info(
                "changeRoomAccountProcess|A账户结转到B账户总金额为" + totalTransferMoney + ",冻结金额为" + totalFronzenMoney + ",结转完毕");
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.info("changeRoomAccountProcess|A账户结转到B账户发生错误.", e);
            throw LandseaException.createException(PaymentResultCode.E00150037, e);
        }
    }
    
    /**
     * 
     * [简要描述]：换房A合同退房账单退款到余额处理</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-18)</br>
     *
     * @param contractA
     * @throws LandseaException
     *
     */
    public void billMoney2AccountBalance(Contract contractA)
        throws LandseaException
    {
        try
        {
            Integer userId = contractA.getUserId();
            Integer contractIdA = contractA.getId();
            
            LOGGER.info("billMoney2AccountBalance|处理换房A合同退款结转到A账户余额.userId=" + userId + ",contractIdA=" + contractIdA);
            
            // 查询A合同的退租账单
            Bill billA = billReadMapper.queryOffrentBillByContract(contractIdA);
            if (null == billA)
            {
                // 如果为空，从线程变量中获取，如果还为空，那么报错
                billA = (Bill)CurrentThread.get(CHANGE_ROOM_A_BILL);
                if (null == billA)
                {
                    LOGGER.info("billMoney2AccountBalance|A合同无退房账单，处理结束.");
                    return;
                }
            }
            
            /*
             * if (BillStatusEnum.BIST2.getCode().equals(billA.getBillStatus())) {
             * LOGGER.info("billMoney2AccountBalance|A合同退房账单支付完成，无需结余额转到账户."); return; }
             */
            
            double receivableMoney = billA.getReceivableMoney();
            double amountReceived = billA.getAmountReceived();
            double totalMoney = Arith.sub(receivableMoney, amountReceived);
            // 原来是负数，现在要变成正数金额结转
            totalMoney = -totalMoney;
            LOGGER.info("billMoney2AccountBalance|A合同退房账单应收" + receivableMoney + ",已收" + amountReceived + ",共需结转到余额"
                + totalMoney);
            
            // 查询A账户余额
            List<AccountBalance> accountABalanceList = accountBalanceReadMapper.selectListByContract(contractA);
            if (CollectionUtils.isEmpty(accountABalanceList))
            {
                LOGGER.info("billMoney2AccountBalance|A合同无账户，无法结转，处理结束.");
                return;
            }
            // 获取通用余额对象
            accountABalanceList = accountABalanceList.stream()
                .filter(balance -> BalanceType.COMMON.equals(balance.getBalanceType()))
                .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(accountABalanceList))
            {
                LOGGER.info("billMoney2AccountBalance|A账户下无通用余额项，无法结转，处理结束.");
                return;
            }
            AccountBalance accountBalanceCommon = accountABalanceList.get(0);
            // A账户通用余额
            double commonMoney = accountBalanceCommon.getMoney();
            // A账户通用冻结金额
            double commonFronzen = accountBalanceCommon.getFreezeMoney();
            LOGGER.info("billMoney2AccountBalance|A账户通用余额为" + commonMoney + ",其中冻结金额为" + commonFronzen);
            
            Date nowDate = new Date();
            
            AccountBalance accountBalanceUpdate = new AccountBalance();
            Double totalMoneyB = Arith.add(commonMoney, totalMoney);
            accountBalanceUpdate.setId(accountBalanceCommon.getId());
            accountBalanceUpdate.setMoney(totalMoneyB);
            accountBalanceUpdate.editInitCommonField();
            // 更新A账户的通用余额，将钱全转进通用去
            accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
            
            // 生成支付记录
            BillPayment insertBillPayment = new BillPayment();
            insertBillPayment.setMoney(totalMoney);
            insertBillPayment.setBillId(accountBalanceCommon.getAccountId());
            insertBillPayment.setBillType(BillTypeCode.RECHARGE);
            insertBillPayment.setPayType(PayType.BALANCE_AUTOPAY);
            insertBillPayment.setTime(nowDate);
            // 直接就是支付成功的
            insertBillPayment.setStatus(BillPaymentStatus.SUCCESS);
            insertBillPayment.setComments("换房退房账单结转余额");
            insertBillPayment.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
            billPaymentWriteMapper.insertSelective(insertBillPayment);
            
            // 支付成功，添加A充值记录
            AccountRecharge accountRecharge = new AccountRecharge();
            accountRecharge.setWid(StringTools.generateUuid());
            accountRecharge.setAccountId(accountBalanceCommon.getAccountId());
            accountRecharge.setBalanceType(accountBalanceCommon.getBalanceType());
            accountRecharge.setMoney(totalMoney);
            accountRecharge.setAccountBalance(totalMoneyB);
            accountRecharge.setTime(nowDate);
            accountRecharge.setPaymentId(insertBillPayment.getId());
            accountRecharge.setComment("换房退房账单余额结转");
            accountRecharge.setStatus("成功");
            accountRecharge.initCommonField();
            accountRechargeWriteMapper.insertSelective(accountRecharge);
            LOGGER.info("billMoney2AccountBalance|处理换房退房账单余额结转完毕.");
            
            // 更新A退房账单为支付成功
            Bill billUpdate = new Bill();
            billUpdate.setId(billA.getId());
            billUpdate.setBillStatus(BillStatusEnum.BIST2.getCode());
            billUpdate.setAmountReceived(receivableMoney);
            billUpdate.setLastModifiedDtm(new Timestamp(nowDate.getTime()));
            billUpdate.setLastModifiedBy(SecurityUtils.getCurrentLogin().getUserId());
            billWriteMapper.updateByPrimaryKeySelective(billUpdate);
            LOGGER.info("billMoney2AccountBalance|更新A退房账单支付成功完毕.");
            
            // 更新独立账单
            billIndependentService.monUpdateBalanceSheetRent(contractIdA, nowDate);
        }
        catch (LandseaException e)
        {
            LOGGER.error("billMoney2AccountBalance|处理换房退房账单结转余额失败.", e);
        }
        catch (Exception e)
        {
            LOGGER.error("billMoney2AccountBalance|处理换房退房账单结转余额失败.", e);
        }
    }
    
    /**
     * 
     * [简要描述]：转租取消时退还余额到通用</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-01-12)</br>
     *
     * @param userId
     * @param roomId
     * @param money
     * @throws LandseaException
     *
     */
    public void changeRentRebackMoney(Integer userId, Integer roomId, double money)
        throws LandseaException
    {
        // 账单里的账户余额为负的，要退给租客的，所以此处要变成正数
        money = - money;
        LOGGER.info("changeRentRebackMoney|开始处理转租取消时退还余额到通用.userId=" + userId + ",roomId=" + roomId + ",money=" + money);
        try
        {
            Account account = accountReadMapper.selectAccount(roomId, userId);
            if (null == account)
            {
                LOGGER.error("changeRentRebackMoney|转租取消退款到账户账户不存在,处理退出.userId=" + userId + ",roomId=" + roomId);
                throw LandseaException.createException(PaymentResultCode.E00140010);
            }
            Integer accountId = account.getId();
            AccountBalance accountBalance = new AccountBalance();
            accountBalance.setAccountId(accountId);
            accountBalance.setBalanceType(BalanceType.COMMON);
            accountBalance = accountBalanceReadMapper.selectByAccountIdAndType(accountBalance);
            if (null == accountBalance || accountBalance.getDeleted())
            {
                LOGGER.error("changeRentRebackMoney|转租取消退款到账户,通用余额项不存在.userId=" + userId + ",roomId=" + roomId);
                throw LandseaException.createException(PaymentResultCode.E00140010);
            }
            
            double totalMoney = Arith.add(money, accountBalance.getMoney());
            
            Date nowDate = new Date();
            AccountBalance accountBalanceUpdate = new AccountBalance();
            accountBalanceUpdate.setId(accountBalance.getId());
            accountBalanceUpdate.editInitCommonField();
            accountBalanceUpdate.setMoney(totalMoney);
            accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
            
            // 生成支付记录
            BillPayment insertBillPayment = new BillPayment();
            insertBillPayment.setMoney(money);
            insertBillPayment.setBillId(accountId);
            insertBillPayment.setBillType(BillTypeCode.RECHARGE);
            insertBillPayment.setPayType(PayType.BALANCE_AUTOPAY);
            insertBillPayment.setTime(nowDate);
            // 直接就是支付成功的
            insertBillPayment.setStatus(BillPaymentStatus.SUCCESS);
            insertBillPayment.setComments("取消转租退还余额");
            insertBillPayment.updateCommonInfo(-1);
            billPaymentWriteMapper.insertSelective(insertBillPayment);
            
            // 支付成功，添加充值记录
            AccountRecharge accountRecharge = new AccountRecharge();
            accountRecharge.setWid(StringTools.generateUuid());
            accountRecharge.setAccountId(accountBalance.getAccountId());
            accountRecharge.setBalanceType(accountBalance.getBalanceType());
            accountRecharge.setMoney(money);
            accountRecharge.setAccountBalance(totalMoney);
            accountRecharge.setTime(nowDate);
            accountRecharge.setPaymentId(insertBillPayment.getId());
            accountRecharge.setComment("取消转租退还余额");
            accountRecharge.setStatus("成功");
            accountRecharge.initCommonField();
            accountRechargeWriteMapper.insertSelective(accountRecharge);
            
            LOGGER.info("changeRentRebackMoney|取消转租退还余额结束.AccountId=" + accountId);
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("changeRentRebackMoney|取消转租退还余额失败.userId=" + userId + ",roomId=" + roomId);
            throw LandseaException.createException(PaymentResultCode.E00150038, e);
        }
    }
    
    /**
     * 
     * [简要描述]：转租最终清空冻结金额</br>
     * [详细描述]：在退款申请提交后</br>
     * [作者]：Aaron(2018-01-12)</br>
     *
     * @param userId
     * @param roomId
     * @throws LandseaException
     *
     */
    public void changeRentClearFrozenMoney(Integer userId, Integer roomId)
        throws LandseaException
    {
        LOGGER.info("changeRentClearFrozenMoney|开始处理转租清除冻结金额.userId=" + userId + ",roomId=" + roomId);
        try
        {
            Account account = accountReadMapper.selectAccount(roomId, userId);
            if (null == account)
            {
                LOGGER.error("changeRentClearFrozenMoney|账户不存在,处理退出.userId=" + userId + ",roomId=" + roomId);
                return;
            }
            Integer accountId = account.getId();
            
            List<AccountBalance> balanceList = accountBalanceReadMapper.selectBalanceByAccountId(accountId);
            
            if (CollectionUtils.isEmpty(balanceList))
            {
                LOGGER.info("changeRentClearFrozenMoney|账户无余额项，清退结束.AccountId=" + accountId);
                return;
            }
            
            for (AccountBalance accountBalance : balanceList)
            {
                double frozenMoney = accountBalance.getFreezeMoney();
                double money = accountBalance.getMoney();
                
                if (money <= 0.00d && frozenMoney <= 0.00d)
                {
                    continue;
                }
                LOGGER.info("changeRentClearFrozenMoney|清空余额，费项=" + accountBalance.getBalanceType() + ",余额" + money + ",冻结"
                    + frozenMoney);
                
                AccountBalance accountBalanceUpdate = new AccountBalance();
                accountBalanceUpdate.setId(accountBalance.getId());
                accountBalanceUpdate.editInitCommonField();
                accountBalanceUpdate.setMoney(0.00d);
                accountBalanceUpdate.setFreezeMoney(0.00d);
                accountBalanceWriteMapper.updateByPrimaryKeySelective(accountBalanceUpdate);
            }
            
            LOGGER.info("changeRentClearFrozenMoney|转租清空冻结余额结束.AccountId=" + accountId);
        }
        catch (Exception e)
        {
            LOGGER.error("changeRentClearFrozenMoney|转租清空冻结余额失败.userId=" + userId + ",roomId=" + roomId);
            throw LandseaException.createException(PaymentResultCode.E00150039, e);
        }
    }
}
